package com.boot.springBoot.encrypt;

import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import java.security.MessageDigest;
import java.util.Base64;

/**
 * 1. 对称加密（Symmetric Cryptography）
		       对称加密是最快速、最简单的一种加密方式，加密（encryption）与解密（decryption）用的是同样的密钥（secret key）,这种方法在密码学中叫做对称加密算法。对称加密有很多种算法，由于它效率很高，所以被广泛使用在很多加密协议的核心当中。 
		       对称加密解密时间都是非常快的。 
 * 2.常见的对称加密和非对称加密算法
		（1）对称加密算法：
		       在对称加密算法中常用的算法有：DES、3DES、TDEA、Blowfish、RC2、RC4、RC5、IDEA、SKIPJACK、AES等。
		
		       DES（Data Encryption Standard）：数据加密标准，速度较快，适用于加密大量数据的场合。 
		       3DES（Triple DES）：是基于DES，对一块数据用三个不同的密钥进行三次加密，强度更高。 
		       AES（Advanced Encryption Standard）：高级加密标准，是下一代的加密算法标准，速度快，安全级别高；
		
		（2）非对称常用算法
		       RSA、Elgamal、背包算法、Rabin、D-H、ECC（椭圆曲线加密算法）。
	MD5(Message Digest algorithm 5，信息摘要算法)
 *
 * @author ysp
 * @since 2019/5/17 10:46
 */
public class DescUtil {

    private static String AppSecret = "7MAcCYv1";
    private final static byte[] IV = new byte[]{ (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78, (byte) 0x90, (byte)0xAB, (byte)0xCD, (byte)0xEF };
    private static String ENCODE = "UTF-8";
    private static String DES = "DES";

    /**
     *   DES cbc模式  偏移量IV= 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF
     *   偏移量IV 是一个byte数组 
        * @Title: encrypt  
        * @Description: TODO(这里用一句话描述这个方法的作用)  
        * @param @param src
        * @param @return
        * @param @throws Exception    参数  
        * @return String    返回类型  
        * @throws
     */
    public static String encrypt(String src) throws Exception {
        DESKeySpec ks = new DESKeySpec(AppSecret.getBytes(ENCODE));
        SecretKeyFactory skf = SecretKeyFactory.getInstance(DES);
        SecretKey sk = skf.generateSecret(ks);
        Cipher cip = Cipher.getInstance("DES/CBC/PKCS5Padding");//Cipher.getInstance("DES");
        IvParameterSpec iv2 = new IvParameterSpec(IV);
        cip.init(Cipher.ENCRYPT_MODE, sk, iv2);//IV的方式
        String dest = Base64.getEncoder().encodeToString(cip.doFinal(src.getBytes(ENCODE)));
        return dest.replace("/", "@").replace("=", "!").replace("+", "[");
    }

    /**
     * DES cbc模式  偏移量IV= 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF
     *   偏移量IV 是一个byte数组 
     * @param str
     * @return
     */
    public static String decrypt(String str) {
        try {

            if(!StringUtils.isEmpty(str)){
                str = str.replace("@", "/").replace("!", "=").replace("[", "+");
            }
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] data = decoder.decodeBuffer(str);

            // 从原始密匙数据创建一个DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(AppSecret.getBytes(ENCODE));
            // 创建一个密匙工厂，然后用它把DESKeySpec对象转换成
            // 一个SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(dks);
            // .net DES 默认是CBC模式。。。。
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            IvParameterSpec iv2 = new IvParameterSpec(IV);
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv2);
            // 正式执行解密操作
            byte decryptedData[] = cipher.doFinal(data);
            return new String(decryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * MD5(Message Digest algorithm 5，信息摘要算法)
        * @Title: MD5Is32  
        * @Description: TODO(32位的MD5加密    返回16进制的32位字节码)  
        * @param @param src
        * @param @return
        * @param @throws Exception    参数  
        * @return String    返回类型  
        * @throws
     */
    public static String MD5Is32(String src) throws Exception{
        char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        messageDigest.update(src.getBytes());
        //md5:378fa3db4dadb130276e5c72ad313953
        byte[] md = messageDigest.digest();
        // 把密文转换成十六进制的字符串形式
        int j = md.length;
        char str[] = new char[j * 2];
        int k = 0;
        for (int i = 0; i < j; i++) {
            byte byte0 = md[i];
            str[k++] = hexDigits[byte0 >>> 4 & 0xf];
            str[k++] = hexDigits[byte0 & 0xf];
        }
        return new String(str);
    }
    
    /**
     * 加密
     * @param str
     * @param key  AES-128-ECB和CBC加密模式，key需要为16位。
     * @return
     * @throws Exception
     */
    public static String aesEncrypt(String str, String key) throws Exception {
        if (str == null || key == null) return null;
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("utf-8"), "AES"));
        byte[] bytes = cipher.doFinal(str.getBytes("utf-8"));
        return new BASE64Encoder().encode(bytes);
    }

    /**
     * 解密
     * @param str
     * @param key
     * @return
     * @throws Exception
     */
    public static String aesDecrypt(String str, String key) throws Exception {
        if (str == null || key == null) return null;
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes("utf-8"), "AES"));
        byte[] bytes = new BASE64Decoder().decodeBuffer(str);
        bytes = cipher.doFinal(bytes);
        return new String(bytes, "utf-8");
    }


    public static void main(String[] args)throws Exception{

        String str = encrypt("378fa3db4dadb130276e5c72ad313953");
        System.err.println("加密结果是:" + str);
        System.err.println("解密结果是:" + decrypt(str));
        String str2 = "{\"Code\":\"Ipc_GroupReport\",\"AppId\":\"7MAcCYv1\"}";
        System.err.println("MD5加密结果是:" + MD5Is32(str2));
        
        System.err.println("AES加密结果是:" + aesEncrypt("我来了，你走了", "YspAes0123456789"));
        System.err.println("AES解密结果是:" + aesDecrypt("jBB2r+FlTKcP2oqhvaWNMc3Ozm4bXKetvauMyg7ZKYk=", "YspAes0123456789"));
    }
}
